package com.jfinal.ssl.controller;

import com.jfinal.aop.Inject;
import com.jfinal.kit.Ret;
import com.jfinal.ssl.common.CaffeineKit;
import com.tencentcloudapi.common.AbstractModel;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.ssl.v20191205.SslClient;
import com.tencentcloudapi.ssl.v20191205.models.*;

import java.io.*;
import java.net.URL;
import java.util.List;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class TencentServer {
    @Inject
    SslServer sslServer;



    //申请免费SSL证书
    public Ret getFreeSSL(String doMain){
        Ret ret = Ret.ok();
            //校验正确，没有重复申请，才继续申请
            try{
                // 实例化一个认证对象，入参需要传入腾讯云账户 SecretId 和 SecretKey，此处还需注意密钥对的保密
                // 代码泄露可能会导致 SecretId 和 SecretKey 泄露，并威胁账号下所有资源的安全性
                // 以下代码示例仅供参考，建议采用更安全的方式来使用密钥
                // 请参见：https://cloud.tencent.com/document/product/1278/85305
                // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
                Credential cred = new Credential(SslServer.SecretId,SslServer.SecretKey);
                // 使用临时密钥示例
                // Credential cred = new Credential("SecretId", "SecretKey", "Token");
                // 实例化一个http选项，可选的，没有特殊需求可以跳过
                HttpProfile httpProfile = new HttpProfile();
                httpProfile.setEndpoint("ssl.tencentcloudapi.com");
                // 实例化一个client选项，可选的，没有特殊需求可以跳过
                ClientProfile clientProfile = new ClientProfile();
                clientProfile.setHttpProfile(httpProfile);
                // 实例化要请求产品的client对象,clientProfile是可选的
                SslClient client = new SslClient(cred, "", clientProfile);
                // 实例化一个请求对象,每个接口都会对应一个request对象
                ApplyCertificateRequest req = new ApplyCertificateRequest();
                req.setDvAuthMethod("DNS_AUTO");
                req.setDomainName(doMain);
                // 返回的resp是一个ApplyCertificateResponse的实例，与请求对象对应
                ApplyCertificateResponse resp = client.ApplyCertificate(req);
                ret.set("data",resp);
                // 输出json格式的字符串回包
                System.out.println(AbstractModel.toJsonString(resp));
                String certificateId = resp.getCertificateId();
                //需要添加到队列，定时查询是否完成签发，已签发的则下载覆盖到最新位置
                CaffeineKit.put("cacheData",certificateId,certificateId);
            } catch (TencentCloudSDKException e) {
                System.out.println(e.toString());
                ret.setFail().set("data",e.toString());
            }
            return ret;
    }

    //读取ssl列表
    public DescribeCertificatesResponse getSslList() {
        try {
            // 实例化一个认证对象，入参需要传入腾讯云账户 SecretId 和 SecretKey，此处还需注意密钥对的保密
            // 代码泄露可能会导致 SecretId 和 SecretKey 泄露，并威胁账号下所有资源的安全性
            // 以下代码示例仅供参考，建议采用更安全的方式来使用密钥
            // 请参见：https://cloud.tencent.com/document/product/1278/85305
            // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
            Credential cred = new Credential(SslServer.SecretId, SslServer.SecretKey);
            // 使用临时密钥示例
            // Credential cred = new Credential("SecretId", "SecretKey", "Token");
            // 实例化一个http选项，可选的，没有特殊需求可以跳过
            HttpProfile httpProfile = new HttpProfile();
            httpProfile.setEndpoint("ssl.tencentcloudapi.com");
            // 实例化一个client选项，可选的，没有特殊需求可以跳过
            ClientProfile clientProfile = new ClientProfile();
            clientProfile.setHttpProfile(httpProfile);
            // 实例化要请求产品的client对象,clientProfile是可选的
            SslClient client = new SslClient(cred, "", clientProfile);
            // 实例化一个请求对象,每个接口都会对应一个request对象
            DescribeCertificatesRequest req = new DescribeCertificatesRequest();
            req.setLimit(1000L);//显示1000条数据,免费只有50个,基本能满足。
            // 返回的resp是一个DescribeCertificatesResponse的实例，与请求对象对应
            DescribeCertificatesResponse resp = client.DescribeCertificates(req);
            // 输出json格式的字符串回包
            System.out.println(AbstractModel.toJsonString(resp));
            return resp;
        } catch (TencentCloudSDKException e) {
            System.out.println(e);
            return null;
        }
    }

    public Ret detailSSL(String certificateId){
        Ret ret = Ret.ok();
        try{
            // 实例化一个认证对象，入参需要传入腾讯云账户 SecretId 和 SecretKey，此处还需注意密钥对的保密
            // 代码泄露可能会导致 SecretId 和 SecretKey 泄露，并威胁账号下所有资源的安全性
            // 以下代码示例仅供参考，建议采用更安全的方式来使用密钥
            // 请参见：https://cloud.tencent.com/document/product/1278/85305
            // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
            Credential cred = new Credential(SslServer.SecretId,SslServer.SecretKey);
            // 使用临时密钥示例
            // Credential cred = new Credential("SecretId", "SecretKey", "Token");
            // 实例化一个http选项，可选的，没有特殊需求可以跳过
            HttpProfile httpProfile = new HttpProfile();
            httpProfile.setEndpoint("ssl.tencentcloudapi.com");
            // 实例化一个client选项，可选的，没有特殊需求可以跳过
            ClientProfile clientProfile = new ClientProfile();
            clientProfile.setHttpProfile(httpProfile);
            // 实例化要请求产品的client对象,clientProfile是可选的
            SslClient client = new SslClient(cred, "", clientProfile);
            // 实例化一个请求对象,每个接口都会对应一个request对象
            DescribeCertificateDetailRequest req = new DescribeCertificateDetailRequest();
            req.setCertificateId(certificateId);
            // 返回的resp是一个DescribeCertificateDetailResponse的实例，与请求对象对应
            DescribeCertificateDetailResponse resp = client.DescribeCertificateDetail(req);
            // 输出json格式的字符串回包
            //System.out.println(AbstractModel.toJsonString(resp));
            if (resp.getStatus() != 1){
                ret.setFail();
            }
            ret.set("data",resp);
        } catch (TencentCloudSDKException e) {
            System.out.println(e.toString());
            ret.setFail().set("msg",e.toString());
        }
        return ret;
    }


    //下载证书
    public Ret downSSL(String certificateId){
        Ret ret = Ret.ok();
        try{
            // 实例化一个认证对象，入参需要传入腾讯云账户 SecretId 和 SecretKey，此处还需注意密钥对的保密
            // 代码泄露可能会导致 SecretId 和 SecretKey 泄露，并威胁账号下所有资源的安全性
            // 以下代码示例仅供参考，建议采用更安全的方式来使用密钥
            // 请参见：https://cloud.tencent.com/document/product/1278/85305
            // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
            Credential cred = new Credential(SslServer.SecretId,SslServer.SecretKey);
            // 使用临时密钥示例
            // Credential cred = new Credential("SecretId", "SecretKey", "Token");
            // 实例化一个http选项，可选的，没有特殊需求可以跳过
            HttpProfile httpProfile = new HttpProfile();
            httpProfile.setEndpoint("ssl.tencentcloudapi.com");
            // 实例化一个client选项，可选的，没有特殊需求可以跳过
            ClientProfile clientProfile = new ClientProfile();
            clientProfile.setHttpProfile(httpProfile);
            // 实例化要请求产品的client对象,clientProfile是可选的
            SslClient client = new SslClient(cred, "", clientProfile);
            // 实例化一个请求对象,每个接口都会对应一个request对象
            DescribeDownloadCertificateUrlRequest req = new DescribeDownloadCertificateUrlRequest();
            req.setCertificateId(certificateId);
            req.setServiceType(SslServer.ServiceType);//nginx
            // 返回的resp是一个DescribeDownloadCertificateUrlResponse的实例，与请求对象对应
            DescribeDownloadCertificateUrlResponse resp = client.DescribeDownloadCertificateUrl(req);
            // 输出json格式的字符串回包
            String url = resp.getDownloadCertificateUrl();
            // 1. 确保目标目录存在
            File sslDir = new File(SslServer.path);
            if (!sslDir.exists()) {
                sslDir.mkdirs();
            }
            // 2. 下载ZIP文件到临时位置
            File tempZip = File.createTempFile("ssl_", ".zip");
            try (InputStream in = new URL(url).openStream();
                 FileOutputStream out = new FileOutputStream(tempZip)) {
                byte[] buffer = new byte[8192];
                int bytesRead;
                while ((bytesRead = in.read(buffer)) != -1) {
                    out.write(buffer, 0, bytesRead);
                }
            }
            System.out.println("ZIP文件下载完成: " + tempZip.getAbsolutePath());

            // 3. 解压ZIP文件到d:\\ssl（支持覆盖）
            try (ZipInputStream zipIn = new ZipInputStream(new FileInputStream(tempZip))) {
                ZipEntry entry;
                while ((entry = zipIn.getNextEntry()) != null) {
                    // 忽略目录条目
                    if (entry.isDirectory()) {
                        zipIn.closeEntry();
                        continue;
                    }

                    // 获取文件名（不含路径）
                    String fileName = new File(entry.getName()).getName();
                    File outputFile = new File(sslDir, fileName);

                    // 检查文件是否存在，准备覆盖
                    if (outputFile.exists()) {
                        System.out.println("覆盖现有文件: " + outputFile.getAbsolutePath());
                        // 尝试删除现有文件以确保可以覆盖
                        if (!outputFile.delete()) {
                            System.err.println("警告：无法删除现有文件，可能被占用: " + outputFile.getAbsolutePath());
                            continue; // 跳过无法覆盖的文件
                        }
                    }

                    // 确保父目录存在
                    outputFile.getParentFile().mkdirs();

                    // 写入文件
                    try (FileOutputStream fos = new FileOutputStream(outputFile)) {
                        byte[] buffer = new byte[8192];
                        int len;
                        while ((len = zipIn.read(buffer)) > 0) {
                            fos.write(buffer, 0, len);
                        }
                    }
                    System.out.println("创建/覆盖文件: " + outputFile.getAbsolutePath());
                    zipIn.closeEntry();
                }
            }
            System.out.println("解压完成至: " + SslServer.path);
            // 4. 删除临时ZIP文件
            tempZip.delete();

        } catch (TencentCloudSDKException e) {
            ret.setFail().set("msg",e.toString());
            System.out.println(e.toString());
        } catch (FileNotFoundException e) {
            ret.setFail().set("msg",e.toString());
            throw new RuntimeException(e);
        } catch (IOException e) {
            ret.setFail().set("msg",e.toString());
            throw new RuntimeException(e);
        }
        return ret;
    }

    public Ret deleteSSL(String certificateId){
        Ret ret = Ret.ok();
        try{
            // 实例化一个认证对象，入参需要传入腾讯云账户 SecretId 和 SecretKey，此处还需注意密钥对的保密
            // 代码泄露可能会导致 SecretId 和 SecretKey 泄露，并威胁账号下所有资源的安全性
            // 以下代码示例仅供参考，建议采用更安全的方式来使用密钥
            // 请参见：https://cloud.tencent.com/document/product/1278/85305
            // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
            Credential cred = new Credential(SslServer.SecretId,SslServer.SecretKey);
            // 使用临时密钥示例
            // Credential cred = new Credential("SecretId", "SecretKey", "Token");
            // 实例化一个http选项，可选的，没有特殊需求可以跳过
            HttpProfile httpProfile = new HttpProfile();
            httpProfile.setEndpoint("ssl.tencentcloudapi.com");
            // 实例化一个client选项，可选的，没有特殊需求可以跳过
            ClientProfile clientProfile = new ClientProfile();
            clientProfile.setHttpProfile(httpProfile);
            // 实例化要请求产品的client对象,clientProfile是可选的
            SslClient client = new SslClient(cred, "", clientProfile);
            // 实例化一个请求对象,每个接口都会对应一个request对象
            DeleteCertificateRequest req = new DeleteCertificateRequest();
            req.setCertificateId(certificateId);
            // 返回的resp是一个DeleteCertificateResponse的实例，与请求对象对应
            DeleteCertificateResponse resp = client.DeleteCertificate(req);
            // 输出json格式的字符串回包
            System.out.println("deleteSSL : " + AbstractModel.toJsonString(resp));
        } catch (TencentCloudSDKException e) {
            System.out.println(e.toString());
        }
        return ret;
    }

}
